
#define WIN // scope W32 API
#define MSI // scope MSI API

#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <strsafe.h>

// internet download
#include "wininet.h"  // DeleteUrlCacheEntry, InternetCanonicalizeUrl
#include "urlmon.h"   // URLDownloadToCacheFile

// package trust
#include "wintrust.h"
#include "softpub.h"

// msi installation
//#include "msidefs.h"
//#include "msiquery.h"
//#include "msi.h"

// setup.exe
#include "common.h"
#include "setup.h"
#include "setupui.h"
#include "resource.h"

#include "BinRes.h"



const DWORD lcidLOCALE_INVARIANT = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);

/////////////////////////////////////////////////////////////////////////////
// WinMain -- Application Entry Point
//

extern "C" int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst , __in LPSTR lpszCmdLine, int nCmdShow)
{

//-----------------------------------------------------------------------------------------------------------------
//  VARIABLES
//
//-----------------------------------------------------------------------------------------------------------------
    UINT    uiRet = ERROR_SUCCESS;
    HRESULT hr    = S_OK;

    char *szMsiFile          = 0;
    char *szBaseURL          = 0;
    char *szInstallPath      = 0;
    char *szMsiCacheFile     = 0;
    char *szOperation        = 0;
    char *szProductName      = 0;
    char *szMinimumMsi       = 0;
    char *szProperties       = 0;
    char *szInstProperties   = 0;
    char *szTempPath         = 0;
    char *szFilePart         = 0;
    char *szBase             = 0;
    char *szUpdate           = 0;

    char *szRegisteredMsiFolder = 0;
    char *szMsiDllLocation      = 0;

    char szAppTitle[MAX_STR_CAPTION]    = {0};
    char szError[MAX_STR_LENGTH]        = {0};
    char szText[MAX_STR_CAPTION]        = {0};
    char szBanner[MAX_STR_LENGTH]       = {0};
    char szAction[MAX_STR_LENGTH]       = {0};
    char szUserPrompt[MAX_STR_LENGTH]   = {0};
    char szProductCode[MAX_LENGTH_GUID] = {0};

    char szModuleFile[MAX_PATH]         = {0};
    DWORD dwModuleFileSize       = MAX_PATH;
    
    DWORD dwMsiFileSize          = 0;
    DWORD dwBaseURLSize          = 0;
    DWORD cchInstallPath         = 0;
    DWORD dwMsiCacheFileSize     = 0;
    DWORD dwOperationSize        = 0;
    DWORD dwProductNameSize      = 0;
    DWORD dwMinimumMsiSize       = 0;
    DWORD dwPropertiesSize       = 0;
    DWORD cchInstProperties      = 0;
    DWORD cchTempPath            = 0;
    DWORD dwLastError            = 0;
    DWORD cchReturn              = 0;
    DWORD dwBaseUpdateSize      = 0;
    DWORD dwUpdateSize          = 0;
    DWORD dwResult               = 0;
    DWORD dwType                 = 0;
    DWORD dwProductCodeSize      = MAX_LENGTH_GUID;

    DWORD dwRegisteredMsiFolderSize  = 0;
    DWORD dwMsiDllLocationSize       = 0;

    ULONG ulMsiMinVer        = 0;
    char *szStopScan         = NULL;

    bool        fDelayRebootReq    = false;
    bool        fPatch             = false;
    bool        fQFE               = false;
    bool        fOSSupported       = false;
    emEnum      emExecMode         = emPreset;

    HKEY hInstallerKey = 0;

    HMODULE hMsi = 0;
  
    
    const char * szAdminImagePath = 0;
	std::string exePath="";


//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
    // create our UI object
    CDownloadUI DownloadUI;

    // Load our AppTitle (caption)
    WIN::LoadString(hInst, IDS_APP_TITLE, szAppTitle, sizeof(szAppTitle)/sizeof(char));

    // Obtain path we are running from
    if (0 == WIN::GetModuleFileName(hInst, szModuleFile, dwModuleFileSize))
    {
        // No UI displayed. Silent failure.
        uiRet = GetLastError();
        goto CleanUp;
    }
    DebugMsg("[Info] we are running from --> %s\n", szModuleFile);

    
    
 
    // initialize our UI object with desktop as parent
    DownloadUI.Initialize(hInst, /* hwndParent = */ 0, szAppTitle);
	//We'll have 5 steps
	// 1: Verify OS
	// 2: Set strings
	// 3: Verify MSI version
	// 4. Verify .net fw
	// 5: launch installer exe or msi
	DownloadUI.InitProgressBar(5);
    // Check if we are installing on an OS that supports Windows Installer 3.0
	/*if (IsWow64())
	{
		MessageBox(DownloadUI.GetCurrentWindow(), "This is 64 bit Proc", "Wow64", MB_OK );;
	}
	else
	{
		MessageBox(DownloadUI.GetCurrentWindow(), "This is NOT 64 bit Proc", "Wow64", MB_OK );;
	}
	if ( ExactWindowsPlatform(5, 1, 2) )
	{
		MessageBox(DownloadUI.GetCurrentWindow(), "This is V5.1", "V5.1", MB_OK );;
	}
	if ( ExactWindowsPlatform(5, 2, 2) )
	{
		MessageBox(DownloadUI.GetCurrentWindow(), "This is V5.2", "V5.2", MB_OK );;
	}*/
    fOSSupported = IsOSSupported();
    if(!fOSSupported)
    {
        PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_OS_NOT_SUPPORTED);
        uiRet = ERROR_INSTALL_FAILURE;
        goto CleanUp;
    }
    HANDLE hMutex = 0;

	DownloadUI.IncrementProgressBar(1);
	if (DownloadUI.HasUserCanceled())
        {
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }
    // only run one instance at a time
    if (AlreadyInProgress(hMutex))
    {
        // silently return - correct return code ?
		uiRet = ERROR_INSTALL_ALREADY_RUNNING;
		goto CleanUp;
    }
    
    // determine operation, default (if not present) is INSTALL
    if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_OPERATION, &szOperation, dwOperationSize)))
    {
        ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
        goto CleanUp;
    }
    if (ERROR_SUCCESS != uiRet)
    {
        // set operation to default which is install
        if (szOperation)
            delete [] szOperation;
        szOperation = new char[lstrlen(szDefaultOperation) + 1];
        if (!szOperation)
        {
            ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            goto CleanUp;
        }
        if (FAILED(StringCchCopy(szOperation, lstrlen(szDefaultOperation) + 1, szDefaultOperation)))
        {
            PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
            uiRet = ERROR_INSTALL_FAILURE;
            goto CleanUp;
        }
    }

    // obtain name of product
    if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_PRODUCTNAME, &szProductName, dwProductNameSize)))
    {
        ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
        goto CleanUp;
    }
    if (ERROR_SUCCESS != uiRet)
    {
        // use default
        if (szProductName)
            delete [] szProductName;
        szProductName = new char[MAX_STR_CAPTION];
        if (!szProductName)
        {
            ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            goto CleanUp;
        }
        WIN::LoadString(hInst, IDS_DEFAULT_PRODUCT, szProductName, MAX_STR_CAPTION);
    }

    // set banner text
    WIN::LoadString(hInst, IDS_BANNER_TEXT, szText, MAX_STR_CAPTION);
    StringCchPrintf(szBanner, sizeof(szBanner), szText, szProductName);
    if (irmCancel == DownloadUI.SetBannerText(szBanner))
    {
        ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
        uiRet = ERROR_INSTALL_USEREXIT;
        goto CleanUp;
    }
	DownloadUI.IncrementProgressBar(1);
	if (DownloadUI.HasUserCanceled())
        {
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }
    //// obtain minimum required MSI version
    //if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_MINIMUM_MSI, &szMinimumMsi, dwMinimumMsiSize)))
    //{
    //    ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
    //    goto CleanUp;
    //}
    //else if (ERROR_SUCCESS != uiRet)
    //{
    //    PostResourceNotFoundError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, ISETUPPROPNAME_MINIMUM_MSI);
    //    goto CleanUp;
    //}

    // make sure required Msi version is a valid value -- must be >= 150
    //ulMsiMinVer = strtoul(szMinimumMsi, &szStopScan, 10);
    //if (!szStopScan || (szStopScan == szMinimumMsi) || (*szStopScan != 0) || ulMsiMinVer < MINIMUM_SUPPORTED_MSI_VERSION)
    //{
    //    // invalid minimum version string
    //    PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_VER_STR, szMinimumMsi, MINIMUM_SUPPORTED_MSI_VERSION);
    //    uiRet = ERROR_INVALID_PARAMETER;
    //    goto CleanUp;
    //}

	ulMsiMinVer=150;
    //DebugMsg("[Resource] Minimum Msi Value = %d\n", ulMsiMinVer);


    // compare minimum required MSI version to that which is on the machine
    if (IsMsiUpgradeNecessary(ulMsiMinVer))
    {
        DebugMsg("[Info] Upgrade of Windows Installer is requested\n");

        // make sure this is admin -- must have admin priviledges to upgrade Windows Installer
        if (!IsAdmin())
        {
            PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_REQUIRES_ADMIN_PRIV);
            uiRet = ERROR_INSTALL_FAILURE;
            goto CleanUp;
        }

        // Ask the user if they want to upgrade the installer
        WIN::LoadString(hInst, IDS_ALLOW_MSI_UPDATE, szUserPrompt, MAX_STR_LENGTH);
        if (IDYES != WIN::MessageBox(DownloadUI.GetCurrentWindow(), szUserPrompt, szAppTitle, MB_YESNO|MB_ICONQUESTION))
        {
            // user decided to cancel
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }

        if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_UPDATE, &szUpdate, dwUpdateSize)))
        {
            ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            goto CleanUp;
        }
        else if (ERROR_SUCCESS != uiRet)
        {
            PostResourceNotFoundError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, ISETUPPROPNAME_UPDATE);
            goto CleanUp;
        }            
        
        // determine if we need to download the Windows Installer update package from the web -- based on presence of UPDATELOCATION property
        if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_UPDATELOCATION, &szBase, dwBaseUpdateSize)))
        {
            ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            goto CleanUp;
        }
        else if (ERROR_SUCCESS == uiRet)
        {
            // presence of UPDATELOCATION property indicates assumption of URL source
            if (ERROR_SUCCESS != (uiRet = DownloadAndUpgradeMsi(hInst, &DownloadUI, szAppTitle, szBase, szUpdate, szModuleFile, ulMsiMinVer)))
            {
                if (ERROR_SUCCESS_REBOOT_REQUIRED == uiRet)
                {
                    // successful, but must reboot at end
                    fDelayRebootReq = true;
                }
                else
                    goto CleanUp;
            }
        }
        else
        {
            // lack of UPDATELOCATION property indicates assumption of Media source
            if (ERROR_SUCCESS != (uiRet = UpgradeMsi(hInst, &DownloadUI, szAppTitle, szModuleFile, szUpdate, ulMsiMinVer)))
            {
                if (ERROR_SUCCESS_REBOOT_REQUIRED == uiRet)
                {
                    // successful, but must reboot at end
                    fDelayRebootReq = true;
                }
                else
                    goto CleanUp;
            }
        }
    }

    DebugMsg("[Info] Windows Installer has been upgraded, or was already correct version\n");

	DownloadUI.IncrementProgressBar(1);
	if (DownloadUI.HasUserCanceled())
        {
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }
	if (!IsNetFx40Installed())
	{
		/*if (PromptForNetFWInstall())
		{*/
		 // make sure this is admin -- must have admin priviledges to upgrade .net
        if (!IsAdmin())
        {
            PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_REQUIRES_ADMIN_PRIV);
            uiRet = ERROR_INSTALL_FAILURE;
            goto CleanUp;
        }

		//MessageBox(DownloadUI.GetCurrentWindow(),"First check failed","First check", MB_OK);
		exePath = BinRes::ExtractBinResource("BIN",102,"dotNetFx40_Client_setup.exe");
		//MessageBox(DownloadUI.GetCurrentWindow(),"Extracted file:" ,"First check", MB_OK);
		//MessageBox(DownloadUI.GetCurrentWindow(),exePath.c_str(),"First check", MB_OK);
		STARTUPINFO info={sizeof(info)};
	
		PROCESS_INFORMATION processInfo;
		bool f = CreateProcess(exePath.c_str(), "", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);
		if (f)
			{
				//MessageBox(DownloadUI.GetCurrentWindow(),"Process Created","First check", MB_OK);
				::WaitForSingleObject(processInfo.hProcess, INFINITE);
					CloseHandle(processInfo.hProcess);
					CloseHandle(processInfo.hThread);
			}
		else
		{
			//MessageBox(DownloadUI.GetCurrentWindow(),"Process Failed","First check", MB_OK);
			
		}
		//}
	}
	else
	{
		//MessageBox(DownloadUI.GetCurrentWindow(),"First check SUCCESS","First check", MB_OK);
	}

	DownloadUI.IncrementProgressBar(1);
	if (DownloadUI.HasUserCanceled())
        {
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }
	//check again
	if (!IsNetFx40Installed())
	{
		/*if (DownloadUI.HasUserCanceled())
        {*/
		//MessageBox(DownloadUI.GetCurrentWindow(),"Second check failed","Second check", MB_OK);
            ReportUserUnSuccessfullFwInstall(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        //}
	}
	else
	{
		//MessageBox(DownloadUI.GetCurrentWindow(),"Second check SUCCESS","Second check", MB_OK);
	}
	DownloadUI.IncrementProgressBar(1);
	if (DownloadUI.HasUserCanceled())
        {
            ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
            uiRet = ERROR_INSTALL_USEREXIT;
            goto CleanUp;
        }
    DownloadUI.Terminate();


	exePath = BinRes::ExtractBinResource("BIN",106,"InstallerGUI.exe");
	STARTUPINFO info={sizeof(info)};
	
		PROCESS_INFORMATION processInfo;
		bool f = CreateProcess(exePath.c_str(), lpszCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo);
		if (f)
			{
				//MessageBox(DownloadUI.GetCurrentWindow(),"Process Created","First check", MB_OK);
				/*::WaitForSingleObject(processInfo.hProcess, INFINITE);
					CloseHandle(processInfo.hProcess);
					CloseHandle(processInfo.hThread);*/
			}
		else
		{
			//MessageBox(DownloadUI.GetCurrentWindow(),"Process Failed","First check", MB_OK);
			
		}
  

  
    
CleanUp:

    if (szMsiFile)
        delete [] szMsiFile;
    if (szBaseURL)
        delete [] szBaseURL;
    if (szInstallPath)
        delete [] szInstallPath;
    if (szMsiCacheFile)
    {
        WIN::DeleteUrlCacheEntry(szMsiCacheFile);
        delete [] szMsiCacheFile;
    }
    if (szProductName)
        delete [] szProductName;
    if (szMinimumMsi)
        delete [] szMinimumMsi;
    if (szProperties)
        delete [] szProperties;
    if (szTempPath)
        delete [] szTempPath;
    if (szBase)
        delete [] szBase;
    if (szUpdate)
        delete [] szUpdate;
    if (szRegisteredMsiFolder)
        delete [] szRegisteredMsiFolder;
    if (szMsiDllLocation)
        delete [] szMsiDllLocation;
    if (szOperation)
        delete [] szOperation;
	try
	{
    if(hMutex)
        CloseHandle(hMutex);

    if (hMsi)
        FreeLibrary(hMsi);
	
    DebugMsg("[Info] Setup exit code is %d\n", uiRet);

    if (fDelayRebootReq)
    {
        // need to reboot machine for updating Windows Installer
        WIN::LoadString(hInst, IDS_REBOOT_REQUIRED, szAction, MAX_STR_LENGTH);
        if (IDYES == MessageBox(NULL, szAction, szAppTitle, MB_YESNO|MB_ICONQUESTION))
        {
            // must first aquire system shutdown privileges on NT/Win2K
            AcquireShutdownPrivilege();
            // initiate system shutdown for reboot
            WIN::ExitWindowsEx(EWX_REBOOT, PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION);
        }
    }
	}
	
	catch (char *str)
	{}
    //return uiRet;
	return 0;
}

